package com.sinosoft.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.ArrayList;
import java.util.List;

/**
 * <descption></descption>
 * @date ：2017/6/14 14:25
 * @author：梅海波<meihaibo13186@sinosoft.com.cn>
 * @version:0.1
 */
public abstract class AbstractBaseDAO {

    @Autowired
    protected JdbcTemplate jdbcTemplate;

    /**
     *
     * @param querySql 查询SQL
     * @param tableNamePrefix 临时表名。实际生成的临时表名会在此名前加“TMP_”，并在此名后加一数字后缀。
     * @param orderByInTmpTable 从临时表获取数据时，可用此参数设定“ORDER BY"
     * @param pageSize 每次从临时表取多少行。
     * @param headList 发送给resultHandler的表头名，用于resultHandler生成报表表头。
     * @param resultHandler 用于处理接收的数据，如将数据写入excel
     * @方法说明 用于查询结果数据量很大，而每次查询费时很多的情况。这种情况下，即使用hibernate分页查询，
     * 每页查询花很久，而且查询次数又多，导致用一般方式查询效率不可接受。
     * 用临时表方法可达到较好效果。如果从临时表获取的数据无需加工转换，在数据量很大时（几十万行以上），
     * 可用此方法一次性完成运行SQL,建临时表存储结果数据，分页从临时表取数据，发送数据给resultHandler，及删除临时表的过程。
     * @date 2015-2-10
     * @author 范强
     */
    protected void fetchDataUsingTmpTable(String querySql, String tableNamePrefix, String orderByInTmpTable, int pageSize,
                                          List<String> headList, IResultPageHandler resultHandler)throws RuntimeException{
        String tmpTableName = null;
        tmpTableName = createTmpTableFromSQL(querySql, tableNamePrefix);
        fetchDataFromTmpTable(tmpTableName, null, headList, pageSize, resultHandler, orderByInTmpTable);
        if (tmpTableName != null) {
            dropTmpTable(tmpTableName);
        }
    }
    /**
     * @param tmpTableName
     * @param overrideSQL 如果从临时表中获取数据不用简单的“SELECT * FROM"而是定制的特殊SQL,用此参数传入定制SQL, 否则传入NULL
     * @param headList 报表表头列表
     * @param pageSize 每页行数
     * @param resultHandler 回调的数据处理对象
     * @param orderByInTmpTable 取数据时查询的”ORDER BY“列
     * @throws Exception
     * @方法说明 从临时表中分页循环获取数据并回调发送给resultHandler
     * @date 2015-2-13
     * @author 范强
     */
    protected void fetchDataFromTmpTable(String tmpTableName, String overrideSQL, List<String> headList, int pageSize,
                                         IResultPageHandler resultHandler, String orderByInTmpTable)throws RuntimeException{

        if (pageSize == 0){
            throw new RuntimeException("pageSize 不应为0！");
        }
        List dataList = null;
        int pageStartRow = 0;

        do {
            dataList = fetchPageFromTmpTable(tmpTableName, overrideSQL, pageStartRow, pageSize, orderByInTmpTable);
            if (resultHandler != null) {
                List headAndData = new ArrayList<List>();
                headAndData.add(headList);
                headAndData.add(dataList);
                resultHandler.handleResultPage(headAndData);
            }
            pageStartRow += pageSize;
        } while (dataList.size() == pageSize);

        if (resultHandler != null) {
            resultHandler.close();
        }

    }

    /**
     *
     * @param tableName 删除的表名
     * @方法说明 用于临时表删除
     * @date 2015-2-10
     * @author 范强
     */
    protected void dropTmpTable(String tableName) throws RuntimeException {
        jdbcTemplate.execute("DROP　TABLE " + tableName);
    }


    /**
     *
     * @param tmpTableName 临时表名
     * @param startRow 本页取数据的起始行
     * @param rowSize 本页取数据的行数
     * @param orderByColumns 取数据时查询的”ORDER BY“列
     * @return 数据列表
     * @throws RuntimeException
     * @方法说明
     * @date 2015-2-10
     * @author 范强
     */
    protected List fetchPageFromTmpTable(String tmpTableName, int startRow, int rowSize, String orderByColumns)
            throws RuntimeException {

        return fetchPageFromTmpTable(tmpTableName, null, startRow, rowSize, orderByColumns);
    }

    /**
     *
     * @param tmpTableName 临时表名
     * @param sqlOverride 定制的SQL由于查询临时表。传入非空值后，会用它作为SQL去查临时表，而不再用通用的“SELECT *　FROM"，orderByColumns参数也不再起作用。
     * @param startRow 本页取数据的起始行
     * @param rowSize 本页取数据的行数
     * @param orderByColumns 取数据时查询的”ORDER BY“列
     * @return 数据列表
     * @throws RuntimeException
     * @方法说明
     * @date 2015-2-10
     * @author 范强
     */
    protected List fetchPageFromTmpTable(String tmpTableName, String sqlOverride, int startRow, int rowSize, String orderByColumns)
            throws RuntimeException {

        String sql = sqlOverride;
        if (sql == null) {
            StringBuilder sqlSb = new StringBuilder();
            sqlSb.append("select * from (select page.*,rownum rn from (");
            sqlSb.append("SELECT *　FROM ").append(tmpTableName);
            if (orderByColumns != null) {
                sqlSb.append(" ORDER BY ").append(orderByColumns);
            }
            sqlSb.append(") page where rownum<=? ) where rn>=?");
            sql = sqlSb.toString();
        }
        return jdbcTemplate.queryForList(sql);

    }

    /**
     *
     * @param sql
     * @param tableNamePrefix
     * @return
     * @throws RuntimeException
     * @方法说明 运行SQL并将结果建立临时表进行存储。用于查询结果数据量很大，而每次查询费时很多的情况。这种情况下，即使用hibernate分页查询，每页查询花很久，而且查询次数又多，导致用一般方式查询效率不可接受。用临时表方法可达到较好效果。
     * @date 2015-2-10
     * @author 范强
     */
    protected String createTmpTableFromSQL(String sql, String tableNamePrefix) throws RuntimeException {

        if (tableNamePrefix == null || tableNamePrefix.length() > 20) {
            throw new RuntimeException("临时表名不应超过20个字符！");
        }
        String sb = " alter session set \"_pred_move_around\"=FALSE ";
        // 加数字后缀以免冲突
        String tableName = "TMP_" + tableNamePrefix + (System.currentTimeMillis() % 1000000);

        StringBuilder sqlSb = new StringBuilder(sql.length() + 50);
        sqlSb.append("CREATE　TABLE ").append(tableName).append(" AS ");
        sqlSb.append(sql);
        jdbcTemplate.execute(sqlSb.toString());

        return tableName;


    }

    protected void fetchDataUsingTmpTable(String querySql, String tableNamePrefix, String orderByInTmpTable, int pageSize,
                                          List<String> headList, IResultPageHandler resultHandler,int objPosition ,int objTotal,int manyColumn) throws RuntimeException {

        String tmpTableName = null;
        tmpTableName = createTmpTableFromSQL(querySql, tableNamePrefix);
        fetchDataFromTmpTable(tmpTableName, null, headList, pageSize, resultHandler, orderByInTmpTable,objPosition,objTotal,manyColumn);
        if (tmpTableName != null) {
            dropTmpTable(tmpTableName);
        }

    }

    protected void fetchDataFromTmpTable(String tmpTableName, String overrideSQL, List<String> headList, int pageSize,
                                         IResultPageHandler resultHandler, String orderByInTmpTable,int objPosition,int objTotal,int manyColumn) throws RuntimeException {
        if (pageSize == 0){
            throw new RuntimeException("pageSize 不应为0！");
        }
        List dataList = null;
        int pageStartRow = 0;

        do {
            dataList = fetchPageFromTmpTable(tmpTableName, overrideSQL, pageStartRow, pageSize, orderByInTmpTable);
            if (resultHandler != null) {
                List headAndData = new ArrayList<List>();
                headAndData.add(headList);
                headAndData.add(dataList);
                resultHandler.handleResult(headAndData,manyColumn);
            }
            pageStartRow += pageSize;
        } while (dataList.size() == pageSize);

        if (resultHandler != null) {
            //判断当前对象是否是最后一个
            if (objPosition==(objTotal-1)) {
                resultHandler.close();
            }
        }

    }
}
